package server;

import interfacePaxos.PaxosRemote;

import java.io.IOException;
import java.rmi.RemoteException;
import java.util.List;

public class Proposer {

  /**
   * @param args
   */

  private int proposerId = 1;
  private int proposal_counter = 1;
  private List<PaxosRemote> my_nodes;
  private boolean lock = false;
  private int lockNumber = -1;
  private Proposal current_proposal;
  private int agreed;
  private int totalResponses;
  private int quorum;
  private String lease_client;

  /*
   * get msg from client
   */
  public Proposer(int proposerID, List<PaxosRemote> nodes)
      throws RemoteException {
    this.proposerId = proposerID;
    my_nodes = nodes;
    proposal_counter = proposerID + 1;
    agreed = 0;
    quorum = (my_nodes.size() / 2) + 1;
  }

  public Proposer() {

  }

  public void request(String the_client, boolean the_lock, int the_lockNumber)
      throws IOException {
    lock = the_lock;
    lockNumber = the_lockNumber;
    lease_client = the_client;
  }

  /*
   * send prepare msg to Acceptors
   */
  public Proposal sendPrepareProposal() throws IOException {
    agreed = 0;
    totalResponses = 0;
    current_proposal = new Proposal(proposal_counter, this.proposerId, lock,
        lockNumber, lease_client);
    for (PaxosRemote remote : my_nodes) {
      remote.prepare(current_proposal);
    }
    return current_proposal;
  }

  /**
   * 
   * @param acceptor_index
   * @param prepared
   * @param p
   * @throws IOException
   */
  public void promise(int acceptor_index, boolean prepared, Proposal p)
      throws IOException {
    totalResponses++;
    if (prepared) {

      agreed++;

      if (agreed >= quorum) { // IF QUORUM AGREES
        sendAcceptRequestToAccepter();
        proposal_counter += my_nodes.size();
      } else { // NO AGREEMENT, Delay and restart Paxos
        if (totalResponses > quorum) {
          this.proposal_counter += my_nodes.size();
        }
        this.current_proposal.setN(this.proposal_counter);
      }

    } else {
      handleNack(p);
    }
  }

  /*
   * handle nack
   */
  public void handleNack(Proposal p) {
    proposal_counter = p.getNumber() + my_nodes.size();
  }

  /**
   * The Proposer sends an Accept Request message to a Quorum of Acceptors with
   * the chosen value for its proposal.
   * 
   * @throws IOException
   */
  public void sendAcceptRequestToAccepter() throws IOException {
    for (PaxosRemote remote : my_nodes) {
      remote.accept(current_proposal);
    }
  }

}
